<?php

namespace bs\tools;
//设置东八区
date_default_timezone_set('Asia/Shanghai');

/**
 * // 设置基础日志目录
 * FileLogs::setBaseDir(__DIR__.'/tmp/logs/');
 *
 * // 设置最大日志文件大小（5MB）
 * FileLogs::setMaxFileSize(5 * 1024 * 1024);
 *
 * // 设置保留 60 天日志
 * FileLogs::setMaxDays(60);
 *
 * // 关闭按天分割日志（改为按大小分割）
 * FileLogs::setSplitByDay(false);
 *
 *
 * FileLogs::info('test/app.log',$args);
 *
 * FileLogs::debug('test.log',"测试一下日志内容");
 *
 * FileLogs::debug('test.log',"测试一下日志：{title},{content}",['title'=>'标题','content'=>'abc']);
 */
class FileLogs
{
    // 日志级别常量
    const EMERGENCY = 'emergency';
    const ALERT = 'alert';
    const CRITICAL = 'critical';
    const ERROR = 'error';
    const WARNING = 'warning';
    const NOTICE = 'notice';
    const INFO = 'info';
    const DEBUG = 'debug';
    const SQL = 'sql';

    // 默认配置
    protected static $baseDir = 'runtime/logs/';
    protected static $maxFileSize = 5 * 1024 * 1024; // 5MB
    protected static $maxDays = 30;//30天后删除
    protected static $splitByDay = true;//按天分割日志(按日分目录)

    protected static $driver = 'file';//保存文件的驱动类型，file=本地，mongodb=mongodb,curl=curl远程

    /**
     * 设置默认日志基础目录
     */
    public static function setBaseDir($dir)
    {
        self::$baseDir = rtrim($dir, '/') . '/';
    }

    /**
     * 设置最大日志文件大小（字节）
     */
    public static function setMaxFileSize($sizeInBytes)
    {
        self::$maxFileSize = $sizeInBytes;
    }

    /**
     * 设置保留日志的最大天数
     */
    public static function setMaxDays($days)
    {
        self::$maxDays = $days;
    }

    /**
     * 设置保存文件的驱动类型
     */
    public static function setDriver($driver)
    {
        self::$driver = $driver;
    }

    /**
     * 设置是否按天分割日志文件
     */
    public static function setSplitByDay($enable)
    {
        self::$splitByDay = (bool)$enable;
    }

    /**
     * 自动清理过期日志
     */
    public static function clearExpiredLogs()
    {
        if (!is_dir(self::$baseDir)) return;

        $cutoffTime = strtotime("-" . self::$maxDays . " days");

        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator(self::$baseDir, \FilesystemIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::SELF_FIRST
        );

        foreach ($iterator as $file) {
            if ($file->isFile() && $file->getMTime() < $cutoffTime) {
                @unlink($file->getPathname());
            }
        }
    }

    /**
     * 记录日志（通用方法）
     *
     * @param string $file 相对于 baseDir 的日志文件路径（如 test/abc.log）
     * @param string $message 日志内容
     * @param string $level 日志级别
     * @param array $context 替换占位符的上下文数据
     */
    public static function record($file, $message, $level = self::INFO, array $context = [])
    {
        // 构建日志内容
        $formattedMessage = self::interpolate($message, $context);
        $logEntry = sprintf("[%s] [%s] %s%s", date('Y-m-d H:i:s'), strtoupper($level), $formattedMessage, PHP_EOL);
        if (self::$driver == 'file') {
            // 清理过期日志（每天只执行一次）
            if (!self::isLogCleanedToday()) {
                self::clearExpiredLogs();
                self::markLogCleanedToday();
            }
            // 构建完整日志路径
            $fullPath = self::$baseDir . ltrim($file, './\\');

            // 解析目录和文件名
            $dir = dirname($fullPath);
            $basename = pathinfo($fullPath, PATHINFO_FILENAME);
            $ext = pathinfo($fullPath, PATHINFO_EXTENSION) ?: 'log';
            if (!is_dir($dir)) {
                mkdir($dir, 0777, true);
            }
            // 按天或按大小生成日志文件名
            if (self::$splitByDay) {
                $date = date('Ymd');
                $dir = $dir . '/' . $date;
                if (!is_dir($dir)) {
                    mkdir($dir, 0777, true);
                }
                $logFile = $dir . '/' . $basename . '.' . $ext;
            } else {
                $logFile = $fullPath;
                if (file_exists($logFile) && filesize($logFile) > self::$maxFileSize) {
                    $counter = 1;
                    while (file_exists($dir . '/' . $basename . '_' . $counter . '.' . $ext)) {
                        $counter++;
                    }
                    $logFile = $dir . '/' . $basename . '_' . $counter . '.' . $ext;
                }
            }

            // 写入日志文件
            file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);
            return true;
        }

        //mongodb
        if (self::$driver == 'mongodb') {
            self::send_mongodb($file, $logEntry, $level, $context);
            return true;
        }

        if (self::$driver == 'curl') {
            // curl发送给远程
            self::send_curl($file, $logEntry, $level, $context);
            return true;
        }

        return false;
    }

    //todo mongodb，自己实现
    private static function send_mongodb($file, $logEntry, $level = self::INFO, array $context = [])
    {
        if (!extension_loaded('mongodb')) {
            echo "FileLogs send_mongodb error: mongodb extension not loaded";
            return;
        }
        $mongodb = new \MongoDB\Client("mongodb://localhost:27017");
        $collection = $mongodb->mydatabase->logs;
        $collection->insertOne([
            'message' => $logEntry,
            'level' => $level,
            'date' => date('Y-m-d H:i:s'),
            'file' => $file,
            'context' => $context
        ]);
    }

    // 发送给远程,todo 自己测试
    private static function send_curl($file, $logEntry, $level = self::INFO, array $context = [])
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, "http://localhost:8080/api/logs");
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode([
            'message' => $logEntry,
            'level' => $level,
            'date' => date('Y-m-d H:i:s'),
            'file' => $file,
            'context' => $context
        ]));
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT_MS, 200);//设置超时时间,单位毫秒
        $result = curl_exec($curl);
        curl_close($curl);
    }

    /**
     * 替换消息中的占位符
     */
    private static function interpolate($message, $context)
    {
        if ($context && is_string($message)) {
            foreach ($context as $key => $val) {
                $message = str_replace('{' . $key . '}', (string)$val, $message);
            }
            return $message;
        }
        if ((is_array($message) || is_object($message))) {
            return json_encode($message, JSON_UNESCAPED_UNICODE);
        }
        return $message;
    }

    /**
     * 检查今天是否已清理过日志
     */
    private static function isLogCleanedToday()
    {
        $flagFile = self::$baseDir . '.log_cleaned';
        if (!file_exists($flagFile)) return false;

        $lastCleanDate = trim(file_get_contents($flagFile));
        return $lastCleanDate === date('Ymd');
    }

    /**
     * 标记今日日志已清理
     */
    private static function markLogCleanedToday()
    {
        $flagFile = self::$baseDir . '.log_cleaned';
        @file_put_contents($flagFile, date('Ymd'));
    }

    // 快捷方法
    public static function debug($file, $message, array $context = [])
    {
        self::record($file, $message, self::DEBUG, $context);
    }

    public static function info($file, $message, array $context = [])
    {
        self::record($file, $message, self::INFO, $context);
    }

    public static function warning($file, $message, array $context = [])
    {
        self::record($file, $message, self::WARNING, $context);
    }

    public static function error($file, $message, array $context = [])
    {
        self::record($file, $message, self::ERROR, $context);
    }

    public static function critical($file, $message, array $context = [])
    {
        self::record($file, $message, self::CRITICAL, $context);
    }

    public static function alert($file, $message, array $context = [])
    {
        self::record($file, $message, self::ALERT, $context);
    }

    public static function emergency($file, $message, array $context = [])
    {
        self::record($file, $message, self::EMERGENCY, $context);
    }

    public static function notice($file, $message, array $context = [])
    {
        self::record($file, $message, self::NOTICE, $context);
    }

    public static function log($file, $message, $level = self::INFO, array $context = [])
    {
        self::record($file, $message, $level, $context);
    }
}
